import { MyMath, Random } from "../../../core/utils";
import Bullet from "../bullet";
import { BulletSide, PhysicsMath } from "../physics_math";
import World from "../world";
import BulletParticleSystemAssembler from "./bullet_particle_system_assembler";

const { executeInEditMode, ccclass, property } = cc._decorator;

@ccclass
@executeInEditMode
export default class BulletParticleSystem extends cc.RenderComponent {
    //物理属性
    @property
    elastic = 1; //0-1 弹力系数
    @property
    destroyWhenHit = false;

    @property
    speed1 = cc.v2();
    @property
    speed1Var = cc.v2();
    @property
    size1 = 10;
    @property
    size2 = 0;
    @property
    life = 1.0;
    @property
    lifeVar = 0.0;
    @property
    cdMax = 0.05;
    @property
    particleTotal = 150;
    @property({
        serializable: true
    })
    _emissionRate = 5; //每秒发射个数。
    @property
    get emissionRate() {
        return this._emissionRate;
    }
    set emissionRate(val) {
        this._emissionRate = val;
        this.cdMax = 1 / this._emissionRate;
    }

    //动态生成的颜色列表
    _colorList: cc.Color[] = [];
    @property({ serializable: true })
    _color1 = cc.Color.WHITE;
    @property({ serializable: true })
    _color2 = cc.Color.WHITE;
    @property
    get color1() {
        return this._color1;
    }
    set color1(val) {
        this._color1 = val;
    }
    @property
    get color2() {
        return this._color2;
    }
    set color2(val) {
        this._color2 = val;
    }

    @property
    _texture = null;
    @property({
        type: cc.Texture2D
    })
    get texture() {
        return this._texture;
    }
    set texture(val) {
        if (this._texture !== val) {
            this._texture = val;
            this.applyTexture();
        }
    }
    @property(World)
    world: World = null;
    @property([Bullet])
    exceptBullets: Bullet[] = [];
    @property({ editorOnly: true })
    preview = false;
    cd = 0;
    particleX: number[] = [];
    particleY: number[] = [];
    particleSpeedX: number[] = [];
    particleSpeedY: number[] = [];
    particleLife: number[] = [];
    particleLifeMax: number[] = [];
    particleContactState: number[] = [];

    override _assembler: BulletParticleSystemAssembler = null;

    addParticle(x: number, y: number, speedX: number, speedY: number, life: number) {
        x += this.node['_worldMatrix'].m[12]; //tx
        y += this.node['_worldMatrix'].m[13]; //ty
        this.particleX.push(x);
        this.particleY.push(y);
        this.particleSpeedX.push(speedX);
        this.particleSpeedY.push(speedY);
        this.particleLife.push(life);
        this.particleLifeMax.push(life);
        this.particleContactState.push(0);
    }

    addWorld(world: World) {
        this.world = world;
    }

    //override
    _resetAssembler() {
        this.setVertsDirty(true);
        this._assembler = new BulletParticleSystemAssembler();
        this._assembler.init(this);
    }

    //override
    _activateMaterial() {
        let material = this.getMaterial(0);
        if (!material) {
            this.disableRender();
            return;
        }

        material = cc.MaterialVariant.create(material, this);
        material.setProperty("texture", this.texture);
        this.setMaterial(0, material);
        this.markForRender(true);
    }

    applyTexture() {
        let material = this.getMaterial(0);
        if (!material) {
            return
        }

        material.setProperty("texture", this.texture);
    }
    protected update(dt: number): void {
        if (!CC_EDITOR || (CC_EDITOR && this.preview)) {
            if (this.particleTotal > this.particleLife.length) {
                this.cd += dt;
                while (this.cd >= this.cdMax) {
                    this.cd -= this.cdMax;
                    this.addParticle(0, 0, this.speed1.x + Random.range(0, this.speed1Var.x), this.speed1.y + Random.range(0, this.speed1Var.y), this.life + Math.random() * this.lifeVar);
                }
            }
        }
        this.setVertsDirty(true);

        let gravityX = this.world?.gravity.x || 0;
        let gravityY = this.world?.gravity.y || 0;

        let boxes: cc.Rect[] = [];
        if (this.world) {
            this.world.bullets.forEach(bullet => {
                if (this.exceptBullets.includes(bullet)) {
                    return;
                }
                boxes.push(bullet.node.getBoundingBoxToWorld());
            })
        }

        for (let i = 0; i < this.particleLife.length;) {
            let life = this.particleLife[i];
            if (life <= 0) {
                this.particleX.splice(i, 1);
                this.particleY.splice(i, 1);
                this.particleSpeedX.splice(i, 1);
                this.particleSpeedY.splice(i, 1);
                this.particleLife.splice(i, 1);
                this.particleLifeMax.splice(i, 1);
            }
            else {
                if (this.particleContactState[i] > 0) {
                    this.particleContactState[i] -= dt;
                }
                //尺寸
                // let size = MyMath.Lerp(this.size1, this.size2, this.particleLife[i] / this.particleLifeMax[i]);
                let boxSize = 10;
                //物理
                if (this.world && this.particleContactState[i] <= 0) {
                    this.particleSpeedX[i] += gravityX / 100;
                    this.particleSpeedY[i] += gravityY / 100;
                    if (boxes.length > 0) {
                        let wp = cc.v2(this.particleX[i], this.particleY[i]);
                        let wrect = cc.rect(wp.x - boxSize / 2, wp.y - boxSize / 2, boxSize, boxSize);
                        for (let boxInd = 0; boxInd < boxes.length; boxInd++) {
                            let box = boxes[boxInd];
                            if (this.destroyWhenHit) {
                                if (cc.Intersection.rectRect(wrect, box)) {
                                    this.particleLife[i] = 0;
                                    break;
                                }
                            }
                            else {
                                let side = PhysicsMath.CheckCollision(wrect, box);
                                switch (side) {
                                    case BulletSide.Top:
                                        if (this.particleSpeedY[i] > 0) {
                                            this.particleSpeedY[i] *= -this.elastic;
                                            this.particleContactState[i] = PCS;
                                            // console.info("Top");
                                        }
                                        else {
                                            // console.warn("Top: this.particleSpeedY[i] <= 0");
                                        }
                                        break;
                                    case BulletSide.Bottom:
                                        if (this.particleSpeedY[i] < 0) {
                                            this.particleSpeedY[i] *= -this.elastic;
                                            this.particleContactState[i] = PCS;
                                            // console.info("Bottom");
                                        }
                                        else {
                                            // console.warn("Bottom: this.particleSpeedY[i] >=0");
                                        }
                                        break;
                                    case BulletSide.Left:
                                        if (this.particleSpeedX[i] < 0) {
                                            this.particleSpeedX[i] *= -this.elastic;
                                            this.particleContactState[i] = PCS;
                                        }
                                        break;
                                    case BulletSide.Right:
                                        if (this.particleSpeedX[i] > 0) {
                                            this.particleSpeedX[i] *= -this.elastic;
                                            this.particleContactState[i] = PCS;
                                        }
                                        break;
                                }
                                if (side !== BulletSide.None) {
                                    break;
                                }
                            }

                        }
                    }
                }

                //速度
                this.particleX[i] += (this.particleSpeedX[i]) * dt;
                this.particleY[i] += (this.particleSpeedY[i]) * dt;
                this.particleLife[i] -= dt;
                i++;
            }
        }

        this._assembler.update(this, dt);
    }
}

const PCS = 0.05;